﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls.Primitives;

namespace System.Controls.Base
{
  public class SelectionController : DependencyObject
  {
    public static readonly DependencyProperty SelectedItemProperty =
      DependencyProperty.Register( "SelectedItem",
        typeof( object ), typeof( SelectionController ),
        new FrameworkPropertyMetadata(
          null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
          OnSelectedItemChanged, CoerceSelectedItem ) );

    public static readonly DependencyProperty SelectedIndexProperty =
      DependencyProperty.Register( "SelectedIndex",
        typeof( int ), typeof( SelectionController ),
        new FrameworkPropertyMetadata(
          -1, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal,
          OnSelectedIndexChanged, CoerceSelectedIndex ), ValidateSelectedIndex );

    public static readonly DependencyProperty SelectedValuePathProperty =
      DependencyProperty.Register( "SelectedValuePath",
        typeof( string ), typeof( SelectionController ),
        new FrameworkPropertyMetadata( string.Empty, OnSelectedValuePathChanged ) );

    public static readonly DependencyProperty SelectedValueProperty =
      DependencyProperty.Register( "SelectedValue",
        typeof( object ), typeof( SelectionController ) );

    private ISelectionOwner Owner { get; set; }

    internal SelectionController( ISelectionOwner owner )
    {
      Owner = owner;
    }

    public object SelectedItem
    {
      get { return GetValue( SelectedItemProperty ); }
      set { SetValue( SelectedItemProperty, value ); }
    }

    public int SelectedIndex
    {
      get { return (int)GetValue( SelectedIndexProperty ); }
      set { SetValue( SelectedIndexProperty, value ); }
    }

    public string SelectedValuePath
    {
      get { return (string)GetValue( SelectedValuePathProperty ); }
      set { SetValue( SelectedValuePathProperty, value ); }
    }

    public object SelectedValue
    {
      get { return GetValue( SelectedValueProperty ); }
      set { SetValue( SelectedValueProperty, value ); }
    }

    private static ISelectionOwner GetOwner( DependencyObject dep )
    {
      var sc = dep as SelectionController;
      return sc != null ? sc.Owner : null;
    }

    #region ... SelectedItem ...

    private static void OnSelectedItemChanged( DependencyObject dep, DependencyPropertyChangedEventArgs arg )
    {
      var so = GetOwner( dep );

      if ( so != null )
        so.SetSelectedItem( arg.NewValue );
    }

    private static object CoerceSelectedItem( DependencyObject dep, object value )
    {
      var sc = dep as SelectionController;
      if ( sc == null )
        return null;

      // ReSharper disable once SuspiciousTypeConversion.Global
      var so = dep as ISelectionOwner;
      if ( so == null )
        return null;

      if ( so.GetGeneratorStatus() != GeneratorStatus.ContainersGenerated )
        so.SetDeferred( value, DeferredType.Item );

      var selectedIndex = sc.SelectedIndex;

      if ( ( selectedIndex > -1 && selectedIndex < so.ItemsCount && so.Items[selectedIndex] == value ) || so.Items.Contains( value ) )
        return value;

      return DependencyProperty.UnsetValue;
    }

    #endregion

    #region ... SelectedIndex ...

    private static bool ValidateSelectedIndex( object value )
    {
      return ( (int)value ) >= -1;
    }

    private static object CoerceSelectedIndex( DependencyObject dep, object value )
    {
      var so = GetOwner( dep );
      if ( so == null )
        return null;

      if ( so.GetGeneratorStatus() != GeneratorStatus.ContainersGenerated )
      {
        if ( ( value is int ) )
          so.SetDeferred( value, DeferredType.Index );
      }

      if ( ( value is int ) && (int)value >= so.ItemsCount )
        return DependencyProperty.UnsetValue;

      return value;
    }

    private static void OnSelectedIndexChanged( DependencyObject dep, DependencyPropertyChangedEventArgs arg )
    {
      var so = GetOwner( dep );
      if ( so == null )
        return;

      var newIndex = (int)arg.NewValue;

      so.SetSelectedIndex( newIndex );
    }

    #endregion

    #region ... SelectedValue ...

    private static void OnSelectedValuePathChanged( DependencyObject dep, DependencyPropertyChangedEventArgs arg )
    {
      var so = GetOwner( dep );
      if ( so == null )
        return;
    }

    #endregion
  }
}
